/********************************************************************
 * Filename:	DeviceDriver.c
 * Description: Bit operations on the device.
 * Group 2:		nachOS
 * Author(s):	Jan Durakiewicz
 * Class:		CSE 325
 * Instructor:	David Duggan
 * Assignment:	Lab #8 File System Implementation
 * Assigned:	4/16/2013
 * Due:			4/30/2013
 ********************************************************************/

#include "DeviceDriver.h"

//int main(){
//	format_device(1024);
//	printf("%d\n",get_free_block());
//	return 0;
//}



SEGTYPE get_overflow_block(SEGTYPE blockNum){
	SEGTYPE blockAddressStart = blockNum * blockSize * 8;
	SEGTYPE blockAddressEnd = blockAddressStart + (blockSize*8);
	return 	read_var_from_bs(blockAddressEnd -		//find out the address of the next block in this FCB sequence
						     blockAddressWidth + 1, 
						     blockAddressWidth);
}

int read_address(SEGTYPE blockNumber, int index){
	int i = 0;
	SEGTYPE currentBlock,			//block # of block to look at
			blockAddressStart,		//byte address of where that block starts
			blockAddressEnd,		//byte address of where that block ends
			lastAddressIndex,		//last possible index for the start of a block address
			currentReadAddress;		//what we're reading right now

	/*Initialize all of the variables*/
	currentBlock = blockNumber;										
	blockAddressStart = currentBlock * blockSize * 8;
	blockAddressEnd = blockAddressStart + (blockSize*8);
	currentReadAddress = FCB_SIZE + blockAddressStart;
	lastAddressIndex = (blockAddressEnd - blockAddressWidth) - blockAddressWidth + 1;

	while(i < index){
		currentReadAddress += blockAddressWidth;
		i++;
		if(currentReadAddress > lastAddressIndex){			//if we've read the whole block, get the next one
			currentBlock = read_var_from_bs(blockAddressEnd - blockAddressWidth + 1, blockAddressWidth);
			blockAddressStart = currentBlock * blockSize * 8;
			blockAddressEnd = blockAddressStart + (blockSize*8);
			currentReadAddress = blockAddressStart;
			lastAddressIndex = (blockAddressEnd - blockAddressWidth) - blockAddressWidth + 1;
		}
	}
	return read_var_from_bs(currentReadAddress, blockAddressWidth);
}

int add_address(SEGTYPE blockNumber, SEGTYPE addressToAdd){
	SEGTYPE currentBlock,		//the block to look through
			blockAddressStart,	//the byte address where that block starts
			blockAddressEnd,	//the byte address where that block ends
			currentReadAddress,	//what byte address is currently being read
			lastAddressIndex,	//the last possible address in the block where a block address can be stored
			overflowAddress;	//the block number of the next block in the FCB

	currentBlock = blockNumber;									//get the current block number		
	blockAddressStart = currentBlock*blockSize*8;				//figure out the bit address of where it starts
	blockAddressEnd = blockAddressStart + (blockSize*8);		//figure out the bit address of where it ends
	overflowAddress = read_var_from_bs(blockAddressEnd -		//find out the address of the next block in this FCB sequence
									   blockAddressWidth + 1, 
									   blockAddressWidth);

	currentReadAddress = FCB_SIZE + blockAddressStart;	//initialize the current read address (where to read file block addresses from)
														//so that it compensates for the FCB data

	/*While there's an overflow block, keep getting the next block...*/
	while(overflowAddress != 0){
		currentBlock = overflowAddress;							//update the overflow address

		blockAddressStart = currentBlock*blockSize*8;			//update where the block starts
		blockAddressEnd = blockAddressStart + (blockSize*8);	//update where the block ends
		currentReadAddress = blockAddressStart;					//update the read address 	

		overflowAddress = read_var_from_bs(blockAddressEnd - blockAddressWidth + 1, blockAddressWidth); //get the next block address of this FCB sequence
	}


	lastAddressIndex = (blockAddressEnd - blockAddressWidth) - blockAddressWidth + 1;	//find out the last possible bit address where a
																						//block address can be written

	while(1){

		if(read_var_from_bs(currentReadAddress,blockAddressWidth) == 0){			//if the current address slot is free
			write_var_to_bs(addressToAdd,currentReadAddress,blockAddressWidth);	//write there
			return 1;															//and we're done
		}

		currentReadAddress += blockAddressWidth;	//update the read address to the next slot	

		if(currentReadAddress > lastAddressIndex){		//if it's the last address
			overflowAddress = get_free_block();			//get another free block
			if(overflowAddress == 0)					//if there isn't one
				return 0;								//throw an error
			write_var_to_bs(overflowAddress,							//if there is, write it to this block
							blockAddressEnd - blockAddressWidth + 1, 
							blockAddressWidth);
			currentBlock = overflowAddress;							//update current block
			blockAddressStart = currentBlock*blockSize*8;			//update where the block starts
			blockAddressEnd = blockAddressStart + (blockSize*8);	//update where the block ends
			currentReadAddress = blockAddressStart;					//update the read address 	
		}
	}
}


void set_address(SEGTYPE blockNumber, int index, SEGTYPE addressToAdd){
	int i = 0;
	SEGTYPE currentBlock,			//block # of block to look at
			blockAddressStart,		//byte address of where that block starts
			blockAddressEnd,		//byte address of where that block ends
			lastAddressIndex,		//last possible index for the start of a block address
			currentReadAddress;		//what we're reading right now

	/*Initialize all of the variables*/
	currentBlock = blockNumber;										
	blockAddressStart = currentBlock * blockSize * 8;
	blockAddressEnd = blockAddressStart + (blockSize*8);
	currentReadAddress = FCB_SIZE + blockAddressStart;
	lastAddressIndex = (blockAddressEnd - blockAddressWidth) - blockAddressWidth + 1;

	while(i < index){
		currentReadAddress += blockAddressWidth;
		i++;
		if(currentReadAddress > lastAddressIndex){			//if we've read the whole block, get the next one
			currentBlock = read_var_from_bs(blockAddressEnd - blockAddressWidth + 1, blockAddressWidth);
			blockAddressStart = currentBlock * blockSize * 8;
			blockAddressEnd = blockAddressStart + (blockSize*8);
			currentReadAddress = blockAddressStart;
			lastAddressIndex = (blockAddressEnd - blockAddressWidth) - blockAddressWidth + 1;
		}
	}
    write_var_to_bs(addressToAdd, currentReadAddress, blockAddressWidth);
}


SEGTYPE get_free_block(){
	SEGTYPE i;
	for(i = lastAllocatedBlock + 1; i != lastAllocatedBlock; i++){
		if(i > blockCount)		//loop around
			i = 0;

		if(get_block_allocation(i) == 0){	//if we've found one, return it
			lastAllocatedBlock = i;
			return i;
		}
	}
	return 0;
}

void write_FCB(FCB* fcb, SEGTYPE blockNumber){
	int i;
	int byteAddress = blockNumber * blockSize * 8;
	//write filename
	for(i = 0; i < 100; i++){
		write_var_to_bs(fcb->filename[i], byteAddress + (i*8),8);
		if(fcb->filename[i] == '\0')
			break;
	}
	//write the rest
	write_var_to_bs(fcb->filesize, byteAddress + 800,18);
	write_var_to_bs(fcb->isDirectory, byteAddress + 818,1);
}

void read_FCB(FCB* buffer, SEGTYPE blockNumber){
	int i;
	int byteAddress = blockNumber * blockSize * 8;
	//read the filename
	for(i = 0; i < 100; i++)
		buffer->filename[i] = (char)read_var_from_bs(byteAddress + (i*8), 8);
	//read the rest
	buffer->filesize = (unsigned int)read_var_from_bs(byteAddress + 800, 18);
	buffer->isDirectory = (char)read_var_from_bs(byteAddress + 818, 1);
}

void write_FSIB(FSIB* fsib){
	int i;
	//write the file system name
	for(i = 0; i < 100; i++){
		write_var_to_bs(fsib->fs_name[i], i*8,8);
		if(fsib->fs_name[i] == '\0')
			break;
	}
	//write the rest
	write_var_to_bs(fsib->block_size, 800,2);
	write_var_to_bs(fsib->max_block, 802,18);
	write_var_to_bs(fsib->root_block, 820, 18);
}

void read_FSIB(FSIB* buffer){
	int i;
	//read fs_name
	for(i = 0; i < 100; i++){
		buffer->fs_name[i] = (char)read_var_from_bs(i*8,8);
	}
	//read the rest
	buffer->block_size = (unsigned char)read_var_from_bs(800,2);	//magic numbers woo!
	buffer->max_block = (unsigned int)read_var_from_bs(802,18);
	buffer->root_block = (unsigned int)read_var_from_bs(820,18);
}

SEGTYPE read_var_from_bs(SEGTYPE startBit, int bitsToRead){
	int i;												
	SEGTYPE readVal = 0;
	/*Copy bits one by one from the BS to the return variable*/
	for(i = startBit; i < startBit + bitsToRead; i++){
		readVal = readVal << 1;
		if(device_read_bit(i) == 1)
			readVal++;
	}
	return readVal;
}

void write_var_to_bs(SEGTYPE toWrite, SEGTYPE startBit, int bitsToWrite){
	if(startBit + bitsToWrite >= DEVICE_SIZE * 8){
		//printf("Out of bounds Write!");
		return;
	}
	if(bitsToWrite == 0)
		return;
	//write the least significant bit one at a time with each function call
	device_write_bit(startBit + bitsToWrite - 1, toWrite%2);	
	write_var_to_bs(toWrite >> 1, startBit, bitsToWrite - 1);  //lions and tigers and recursion, oh my!
}

/* void print_FCB(FCB toPrint){
	printf("FCB : \n");
	printf("\tfilename :\t%s\n",toPrint.filename);
	printf("\tfilesize :\t%d\n",toPrint.filesize);
	printf("\tis directory :\t%d\n",toPrint.isDirectory);
}

void print_FSIB(FSIB toPrint){
	printf("FSIB : \n");
	printf("\tfs_name :\t%s\n",toPrint.fs_name);
	printf("\tblock_size :\t%ul\n",toPrint.block_size);
	printf("\tnum_blocks :\t%ul\n",toPrint.max_block);
	printf("\troot_block :\t%ul\n",toPrint.root_block);
}
*/